代理模式透過包裝物件來管理外部對物件的操作行為。
每名偶像都有一位經紀人,負責管理其工作行程。廠商會透過經紀人洽談合作機會,並透過經紀人取得回覆。廠商不會直接向偶像發送邀約,偶像也不需直接回應廠商。經紀人代表偶像接收需求並做出回應,這就像代理模式中,代理人替目標對象管理需求一樣。
身為一名前端工程師,你應該對響應式變數這個詞十分熟悉。現代的前端框架經常使用響應式系統來處理畫面更新。響應式系統的核心特色在於自動更新,每當響應式變數發生變化,系統就會自動重新計算並更新畫面。透過響應式系統,工程師無需手動操作和更新畫面,大幅提升了開發效率和程式的維護性。
響應式系統有很多實踐方式,其中不乏代理模式的應用,像是 Vue 的 Reactivity API 就是一個經典的例子。讓我們看看,如何透過代理模式來模擬響應式系統的自動更新機制。
這個類別負責扮演變數的代理人。它將變數封裝為私有屬性,並透過 getter 和 setter 來管理存取行為。當變數被修改時,它會自動通知所有訂閱的元件,確保每個元件能即時更新。
class Reactive<T = any> {
private _value: T;
private components: Set<Component>;
constructor(initialValue: T) {
this._value = initialValue;
this.components = new Set();
}
get value() {
return this._value;
}
set value(newValue: T) {
this._value = newValue;
this.notifyComponents();
}
subscribe(component: Component) {
this.components.add(component);
}
unsubscribe(component: Component) {
this.components.delete(component);
}
notifyComponents() {
this.components.forEach((component) => {
component.update(this._value);
});
}
}
這個類別負責建立 UI 元件。元件會在初始化時訂閱指定的響應式變數,並在每次資料更新時自動重新渲染。
class Component<T = any> {
private name: string;
private data: T;
constructor(name: string, reactive: Reactive<T>) {
this.name = name;
this.data = reactive.value;
reactive.subscribe(this);
}
render() {
console.log(`${this.name} component rendered with data:`, this.data);
}
update(newValue: T) {
if (!Object.is(this.data, newValue)) {
this.data = newValue;
this.render();
}
}
}
透過簡單的代辦清單和項目加總來測試響應式系統。我們定義了待辦清單和項目加總的響應式變數,綁定並渲染對應的 UI,然侯模擬資料更新來觸發重新渲染。
class ReactiveTestDrive {
static main() {
const todo = new Reactive(["Go grocery shopping"]);
const counter = new Reactive(todo.value.length);
const todoList = new Component("TodoList", todo);
const todoCounter = new Component("TodoCounter", counter);
todoList.render();
todoCounter.render();
todo.value = [...todo.value, "Do the laundry"];
counter.value = todo.value.length;
todo.value = [...todo.value, "Clean the house"];
counter.value = todo.value.length;
}
}
ReactiveTestDrive.main();
執行結果。
TodoList component rendered with data: [ 'Go grocery shopping' ]
TodoCounter component rendered with data: 1
TodoList component rendered with data: [ 'Go grocery shopping', 'Do the laundry' ]
TodoCounter component rendered with data: 2
TodoList component rendered with data: [ 'Go grocery shopping', 'Do the laundry', 'Clean the house' ]
TodoCounter component rendered with data: 3
代理人是客戶端程式和實際對象之間的溝通橋樑。它負責協調兩者之間的互動行為,所有向實體物件發出的請求都必須通過它來實現。代理人可以管理客戶端對實際對象的操作行為,並藉由攔截請求來執行額外的邏輯,像是驗證資料或紀錄事件。代理模式能夠輕鬆擴充實際對象的功能,並保持原有行為的獨立性與完整性,讓我們更為靈活地設計程式。
https://github.com/chengen0612/design-patterns-typescript/blob/main/patterns/structural/proxy.ts